﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.XLANGs.BaseTypes;

namespace BizTalk.ESB.Extensions.ExceptionHandling
{
    [Serializable]
    public class ExceptionUtil
    {
        private FaultMessage _fault;

        public ExceptionUtil()
        {
            this._fault = null;
        }

        public void CreateEsbFaultMessage(String ActivityID, String BizTalkApplicationName, String Scope, String Description
            , Exception Error, String FailureCategory, String FaultCode, String FaultDescription, Int32 FaultSeverity, String FaultGenerator
            , String ServiceName)
        {
            // Create Object for FaultMessage
            FaultMessage fault = new FaultMessage();
            fault.ExceptionObject = new FaultMessageExceptionObject();
            fault.Header = new FaultMessageHeader();

            fault.ExceptionObject.Message = Error.Message;
            if (Error.InnerException != null)
            {
                fault.ExceptionObject.InnerExceptionMessage = Error.InnerException.Message;
            }
            else
            {
                fault.ExceptionObject.InnerExceptionMessage = String.Empty;
            }
            fault.ExceptionObject.Source = Error.Source;
            fault.ExceptionObject.StackTrace = Error.StackTrace;
            fault.ExceptionObject.TargetSite = Error.TargetSite.Name;
            fault.ExceptionObject.Type = Error.GetType().Name;

            fault.Header.ActivityIdentity = ActivityID;
            fault.Header.Application = BizTalkApplicationName;
            fault.Header.DateTime = System.DateTime.Now.ToString();
            fault.Header.Description = Description;
            fault.Header.ErrorType = Error.GetType().Name;
            fault.Header.FailureCategory = FailureCategory;
            fault.Header.FaultCode = FaultCode;
            fault.Header.FaultDescription = FaultDescription;
            fault.Header.FaultGenerator = FaultGenerator;
            fault.Header.FaultSeverity = FaultSeverity;
            fault.Header.MachineName = System.Environment.MachineName;
            fault.Header.MessageID = Guid.NewGuid().ToString();
            fault.Header.Scope = Scope;
            fault.Header.ServiceInstanceID = Guid.NewGuid().ToString();
            fault.Header.ServiceName = ServiceName;
            fault.Header.ControlBit = "1";

            this._fault = fault;
        }

        public void AddXlangsMessage(XLANGMessage OrchestrationMessage, String RoutingLocation, Boolean ThrowExceptionOnError)
        {
            if (OrchestrationMessage == null)
            {
                throw new ArgumentNullException("OrchestrationMessage");
            }
            if (RoutingLocation == null)
            {
                throw new ArgumentNullException("RoutingLocation");
            }

            try
            {
                FaultMessage fault = this._fault;
                List<FaultMessageMessage> messages;

                // If fault already has messages, copy array to list.  If not, create new list
                if (fault.Messages == null)
                {
                    messages = new List<FaultMessageMessage>();
                }
                else
                {
                    messages = new System.Collections.Generic.List<FaultMessageMessage>(fault.Messages);
                }

                // Create new fault message from XlangsMessage
                FaultMessageMessage msg = new FaultMessageMessage();
                msg.ContentType = this.GetMessageContentType(OrchestrationMessage);
                msg.InterchangeID = Convert.ToString(OrchestrationMessage.GetPropertyValue(typeof(BTS.InterchangeID)));
                msg.MessageContext = this.GetMessageContext(OrchestrationMessage);
                msg.MessageData = this.GetMessageData(OrchestrationMessage);
                msg.MessageID = Convert.ToString(OrchestrationMessage.GetPropertyValue(typeof(BTS.MessageID)));
                msg.MessageName = OrchestrationMessage.Name;
                msg.MessageType = Convert.ToString(OrchestrationMessage.GetPropertyValue(typeof(BTS.MessageType)));
                msg.RoutingURL = RoutingLocation;
                messages.Add(msg);

                // Add new message to fault message array
                fault.Messages = messages.ToArray();
                this._fault = fault;
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("Exception from " + System.Reflection.Assembly.GetExecutingAssembly().FullName
                    + ": " + e.Message);
                if (ThrowExceptionOnError)
                {
                    throw e;
                }
            }
            finally
            {
            }
        }

        private String GetMessageContentType(XLANGMessage Message)
        {
            XmlDocument msg = (XmlDocument)Message[0].RetrieveAs(typeof(XmlDocument));

            if (msg.DocumentElement.FirstChild.Name.ToUpper() == "STRING")
            {
                return "text/plain";
            }
            else
            {
                return "text/xml";
            }
        }

        private FaultMessageMessageContextProperty[] GetMessageContext(XLANGMessage Message)
        {
            List<FaultMessageMessageContextProperty> msgContextList = new List<FaultMessageMessageContextProperty>();

            System.Reflection.Assembly[] array = null;
            try
            {
                array = AppDomain.CurrentDomain.GetAssemblies();
                if (array == null && array.Length < 1)
                {
                    throw new ArgumentNullException("assemblies");
                }
                for (int i = 0; i < array.Length; i++)
                {
                    if (array[i] != null)
                    {
                        object[] customAttributes = array[i].GetCustomAttributes(typeof(BizTalkAssemblyAttribute), false);
                        if (customAttributes.Length >= 1)
                        {
                            Type[] types = array[i].GetTypes();
                            if (types != null)
                            {
                                for (int j = 0; j < types.Length; j++)
                                {
                                    if (types[j].BaseType == typeof(MessageContextPropertyBase) || types[j].BaseType == typeof(MessageDataPropertyBase))
                                    {
                                        String msgProperty = this.GetMessageContextProperty(Message, types[j]);
                                        if (msgProperty != null)
                                        {

                                            msgContextList.Add(new FaultMessageMessageContextProperty
                                            {
                                                Name = types[j].Name,
                                                Type = types[j].Namespace,
                                                Value = msgProperty
                                            });
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Microsoft.Practices.ESB.Exception.Management.EventLogger.Write("Error accessing message context properties.", e);
            }
            return msgContextList.ToArray();
        }

        private String GetMessageContextProperty(XLANGMessage Message, Type PropertyType)
        {
            try
            {
                return Convert.ToString(Message.GetPropertyValue(PropertyType));
            }
            catch (Microsoft.XLANGs.RuntimeTypes.InvalidPropertyTypeException ePropType)
            {
                Microsoft.Practices.ESB.Exception.Management.EventLogger.Write("Error accessing property value.", ePropType);
                return String.Empty;
            }
            catch (Exception e)
            {
                Microsoft.Practices.ESB.Exception.Management.EventLogger.Write("Error accessing property value.", e);
                return String.Empty;
            }
        }

        private FaultMessageMessageMessageData GetMessageData(XLANGMessage Message)
        {
            XmlDocument msgXml = (XmlDocument)Message[0].RetrieveAs(typeof(XmlDocument));
            FaultMessageMessageMessageData msgData = new FaultMessageMessageMessageData();
            msgData.Any = new XmlNode[1];
            msgData.Any[0] = msgXml;
            return msgData;
        }

        public FaultMessage GetFault()
        {
            return this._fault;
        }

        public XmlDocument GetFaultAsXml()
        {
            // Serialize fault into xml
            XmlDocument xmlFault = BizTalk.ESB.Extensions.Helpers.SerializationHelpers.SerializeAnObject(this._fault);
            return xmlFault;
        }

        public XLANGMessage GetFaultForPublishing(XLANGMessage EsbFaultMessageXmlDocument)
        {
            XLANGMessage xlangFault = null;

            //  Call Receive Pipeline to generate XLANGMessage and set properties
            Microsoft.XLANGs.Pipeline.ReceivePipelineOutputMessages msgs = Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteReceivePipeline(
                typeof(Microsoft.BizTalk.DefaultPipelines.XMLReceive), EsbFaultMessageXmlDocument);
            msgs.MoveNext();
            msgs.GetCurrent(xlangFault);

            return xlangFault;
        }

        public static XLANGMessage PromoteFaultCode(XLANGMessage EsbFaultMessage, Boolean DebugFlag)
        {
            XmlDocument msgXml = (XmlDocument)EsbFaultMessage[0].RetrieveAs(typeof(XmlDocument));
            Object faultCode = BizTalk.ESB.Extensions.Helpers.XmlTools.GetSingleValue(msgXml, @"/*[local-name()='FaultMessage']/*[local-name()='Header']/*[local-name()='FaultCode']", DebugFlag);

            EsbFaultMessage[0].SetPartProperty(typeof(Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property.FaultCode), faultCode);

            return EsbFaultMessage;
        }
    }
}
